home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / mcomm540.zip / SMALTERM.CPP < prev    next >
C/C++ Source or Header  |  1991-01-10  |  60KB  |  1,572 lines

  1.  
  2. /////////////////////////////////////////////////////////////////////////////
  3. //     S M A L T E R M  --  D e m o   D r i v e r   f o r   M C O M M      //
  4. //                         A s y n c   R o u t i n e s                     //
  5. //                                                                         //
  6. //                           Zortech C++ 2.0 Version                       //
  7. //                           Turbo C++ 1.0 Version                         //
  8. //                                                                         //
  9. //             Mike Dumdei, 6 Holly Lane, Texarkana TX 75503               //
  10. //                  Split Up the Middle BBS, 903 838-6713                  //
  11. //                                                                         //
  12. //              (Link with comm_s.lib for external functions)              //
  13. //                     ztc smalterm.cpp comm.cpp comm_s.lib                //
  14. //                     tcc smalterm.cpp comm.cpp comm_s.lib                //
  15. /////////////////////////////////////////////////////////////////////////////
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <ctype.h>
  19. #include <string.h>
  20. #include <process.h>
  21. #include <stdarg.h>
  22. #include <dos.h>
  23. #include <bios.h>
  24. #include <signal.h>
  25.  
  26. #include "comm.hpp"                 // header for AsyncPort class
  27. #include "timer.hpp"                // header for Timer class
  28. #include "ansivid.hpp"              // header for ANSI display functions
  29. #define KEY_INT
  30. #include "keys.h"                   // key definition header
  31. #include "colors.h"                 // vid mode & color definition header
  32. #include "extra.h"                  // extra functions header
  33.  
  34. #if defined(__TURBOC__)             // Turbo C++
  35.   #define KBHIT   bioskey(1)
  36.   #define KBREAD  bioskey(0)
  37.   #define strcmpl stricmp
  38.   #include <io.h>
  39. #else                               // Zortech C++
  40.   #define KBHIT   _bios_keybrd(1)
  41.   #define KBREAD  _bios_keybrd(0)
  42. #endif
  43.  
  44. #define NO_INPUT     (-1)
  45. #define ESC_PRESSED  (-2)
  46. #define TIMED_OUT    (-3)
  47. #define NO_CARRIER   (-4)
  48. #define ERROR_LIMIT  (-5)
  49. #define GOT_CANNED   (-6)
  50. #define BLK_COMP_ERR (-7)
  51. #define BLK_SEQ_ERR  (-8)
  52. #define CHECKSUM_ERR (-9)
  53.  
  54. #define TERMINAL       0
  55. #define HOSTMODE       1            // hostmode isn't in here yet
  56.  
  57. #define SOH         '\x01'
  58. #define STX         '\x02'
  59. #define EOT         '\x04'
  60. #define ACK         '\x06'
  61. #define NAK         '\x15'
  62. #define CAN         '\x18'
  63.  
  64. ///////////////////////////////////////////////////////////////
  65. //  function declarations in order found in source (random)  //
  66. //:////////////////////////////////////////////////////////////
  67. // void ControlBreakHandler(int sig);
  68. int ProcessKeyPress();
  69. void ProcessExtendedKey(int LongCh);
  70. int ProcessAsyncChar();
  71. int SaveScreen();
  72. void RestoreScreen();
  73. void VideoStatus(int saving);
  74. void ClearScreen();
  75. void ToggleLogFile();
  76. void ToggleEcho();
  77. void ToggleLineFeeds();
  78. void ToggleDtr();
  79. void DialNumber();
  80. void DOS_Shell();
  81. void DOS_Command();
  82. void HostMode();
  83. void MessageUpload();
  84. void DSZUpload();
  85. void DSZDownload();
  86. void RunBatchFile(char *FileName);
  87. void SendFile();
  88. void ReceiveFile();
  89. void ExitProgram();
  90. int DisplayHelp();
  91. void DisplayParameters();
  92. void ChangeParameters();
  93. int DrawBox(int Top, int Lft, int Height, int Length);
  94. void RemoveBox();
  95. int Prompt(char *Response, char *PromptMsg, int MaxResponse);
  96. void HangUp();
  97. int MonitoredDelay(int Ticks, int MonitorCarrier);
  98. int WaitFor(char *String, int Ticks, int MonitorCarrier);
  99. void vDisplay(char *Format, ...);
  100. void vDisplay(int Color, char *Format, ...);
  101. void vDisplay(int Row, int Col, char *Format, ...);
  102. int PushScreen(int Row, int Col, int NbrRows, int NbrCols);
  103. int PopScreen();
  104. void ReceiveXmodem(char *FileName);
  105. void SendXmodem(char *FileName, int BlockSize = 128);
  106. void XmodemExitMsg(int rval);
  107. int RxWithTimeOut(int Ticks);
  108. void SetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params);
  109. void ResetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params);
  110. void SendCANs();
  111. int WaitForBlockToEnd(int Ticks);
  112. int GetHdrChar(int Ticks);
  113. void TransferWindow(int Flag);
  114.  
  115. ////////////////////////
  116. //  global variables  //
  117. ////////////////////////
  118. AsyncPort port(4096, 2048);     // port with 4K transmit, 2K receive buffers
  119. char port_name[5];              // ASCII "COM1" or "COM2"
  120. int port_number;                // assigned number COM1==0, COM2==1
  121. int Mode;                       // identifies to subroutines HOST or TERMINAL
  122. FILE *LogFile = NULL;           // handle for log file
  123. char DialString[40];            // storage for dial string
  124. int LineFeeds = 0;              // supply LineFeeds flag
  125. int Echo = 0;                   // Echo on flag
  126. int Dtr = 1;                    // DTR signal level flag
  127. int cyn, hred, grn, hgrn;       // video color variables to enable support
  128. int wht, hblu, ylw;             //   for either color or mono system
  129. char vDispBuf[128];             // buffer for formatted display functions
  130. char *ScreensArray[10];         // array of pointers for Push/Pop Screen
  131. char ScreenIndex = 0;           // index of current pointer in above array
  132.  
  133.  
  134. /////////////////////////////////////////////////
  135. //  main                                       //
  136. //://////////////////////////////////////////////
  137. int cdecl main (int argc, char *argv[])
  138. {
  139.     char params[10], *pc;
  140.     int got_port = 0, return_value, i;
  141.  
  142.     ////////////////////////////////////
  143.     // process command line arguments //
  144.     ////////////////////////////////////
  145.     *params = '\0';                             // preset params to nul string
  146.     for (i = 1; i < argc; i++)
  147.     {
  148.         pc = argv[i];                           // pointer to argument
  149.         if (*pc == '-' || *pc == '/')
  150.             ++pc;                               // skip any switch characters
  151.         if (got_port == 0)
  152.         {
  153.             if (strcmpl(argv[i], "COM1") == 0)      // check for "COM1"
  154.                 got_port = 1;
  155.             else if (strcmpl(argv[i], "COM2") == 0) // check for "COM2"
  156.                 got_port = 1, port_number = COM2;
  157.         }
  158.         if (isdigit(*pc) && strlen(pc) < 10)    // check for parameter string
  159.             strupr(strcpy(params, argv[i]));
  160.     }
  161.     strcpy(port_name, (port_number == COM1) ? "COM1" : "COM2");
  162.  
  163.     ///////////////////////////
  164.     // initialize the video  //
  165.     ///////////////////////////
  166.     initvid();                          // initialize lowlevel video variables
  167.     if (v_mode != MONO && v_mode != CO80)
  168.         setvmode(CO80);                 // reset video mode if necessary
  169.     if (v_mode == CO80)
  170.     {                                   // attributes to use if color system
  171.         cyn = CYN, hred = H_RED, grn = GRN, hgrn = H_GRN,
  172.         wht = WHT, hblu = H_BLU, ylw = YLW;
  173.     }
  174.     else
  175.     {                                   // attributes to use if mono system
  176.         cyn = WHT, hred = H_WHT, grn = WHT, hgrn = H_WHT,
  177.         wht = WHT, hblu = H_WHT, ylw = H_WHT;
  178.     }
  179.     SaveScreen();
  180.  
  181.     /////////////////////////////
  182.     // display sign on message //
  183.     /////////////////////////////
  184.     Display(hred, "SMALTERM - Sample driver for MCOMM library functions\n");
  185.     Display(grn, "    Mike Dumdei, 6 Holly Lane, Texarkana TX 75503\n");
  186.     Display("    Split Up the Middle BBS     903 838-6713\n");
  187.     Display(hred, "Usage: smalterm {COM1 | COM2} {param str EX: 2400N81}\n\n");
  188.     v_color = cyn;
  189.  
  190.     //////////////////////////
  191.     // open the serial port //
  192.     //////////////////////////
  193.     return_value = port.Open(port_number, params);
  194.     if (return_value != 0)                      // if port open failed
  195.     {
  196.         vDisplay(
  197.           "SMALTERM: Can't open %s, Parameters: %s, Error code: %04X\n",
  198.            port_name, (*params) ? params : "default", return_value);
  199.         exit(return_value);
  200.     }                                           // if port open succeeded
  201.     vDisplay("SMALTERM: %s -- PORT OPENED\n\n", port_name);
  202.     Display("Press F1 for HELP screen\n");
  203.  
  204.     //////////////////////////
  205.     // miscellaneous set up //
  206.     //////////////////////////
  207.     tickhookset(1);                     // enable master tick in Timer class
  208. /*  ControlBreakHandler(SIGINT);        // initialize control-break handler */
  209.     SetWindowSize(0, 0, 23, 79);        // set window to protect status line
  210.     Fill(24, 0, '═', cyn, 80);          // display status line
  211.     Display(24, 60, hgrn, port_name);
  212.     DisplayParameters();
  213.     Display(24, 10, hgrn, "DTR");
  214.     *DialString = '\0';                 // clear out dial string
  215.     Mode = TERMINAL;                    // terminal, e.g. not host mode
  216.  
  217.     ///////////////////////////////////
  218. //////       MAIN PROGRAM LOOP       //////
  219.     ///////////////////////////////////
  220.     while (1)
  221.     {
  222.         if (KBHIT)                      // process keyboard character if a
  223.             ProcessKeyPress();          //  key was pressed
  224.         if (port.RxLevel() != 0)        // process character from serial
  225.             ProcessAsyncChar();         //  port if one is available
  226.     }
  227. }
  228.  
  229. /*
  230. /////////////////////////////////////////////////
  231. //  ControlBreakHandler                        //
  232. //://////////////////////////////////////////////
  233. void ControlBreakHandler(int sig)
  234. {
  235.     signal(SIGINT, ControlBreakHandler);
  236. }
  237. */
  238.  
  239. /////////////////////////////////////////////////
  240. //  ProcessKeyPress                            //
  241. //://////////////////////////////////////////////
  242. int ProcessKeyPress()
  243. {
  244.     int LongCh = KBREAD;                // scan code + character
  245.     char Ch = (char)LongCh;             // character without scan code
  246.  
  247.     if (Ch != '\0')                     // if normal key
  248.     {
  249.         port.Tx(Ch);                    //   send it out the serial port
  250.         if (Echo && Ch != ESC)          //   if echo on & not ESC (don't want
  251.         {                               //    to start an ANSI sequence),
  252.             VideoStatus(1);             //   save video status
  253.             if (Ch == '\r' && LineFeeds)
  254.                 Ch = '\n';              //   make it a LF if CR & LF toggle
  255.             Display(Ch);                //    display the char,
  256.             if (LogFile)                //    and write it the log file if
  257.                 fputc(Ch, LogFile);     //    its open
  258.             VideoStatus(0);             //   restore video status
  259.         }
  260.     }
  261.     else                                // extended key, e.g. F1, ALT_X, etc,
  262.         ProcessExtendedKey(LongCh);
  263.     return (LongCh);                    // return the key pressed
  264. }
  265.  
  266. /////////////////////////////////////////////////
  267. //  ProcessExtendedKey                         //
  268. //://////////////////////////////////////////////
  269. void ProcessExtendedKey(int LongCh)
  270. {
  271.     static struct JUMP_TABLE
  272.     {
  273.         int KeyCode;
  274.         void (*Function)();
  275.     }
  276.       TERMINAL_Table[] = {
  277.         { ALT_C, ClearScreen },     { ALT_E, ToggleEcho },
  278.         { ALT_X, ExitProgram },     { ALT_S, DOS_Shell },
  279.         { ALT_D, DialNumber },      { PGUP, SendFile },
  280.         { PGDN, ReceiveFile },      { ALT_H, HangUp },
  281.         { ALT_L, ToggleLineFeeds }, { F2, ToggleLogFile },
  282.         { F10, HostMode },          { ALT_P, ChangeParameters },
  283.         { F4, DOS_Command },        { F3, MessageUpload },
  284.         { F5, DSZUpload },          { F6, DSZDownload },
  285.         { 0, NULL } },
  286.       HOSTMODE_Table[] = {
  287.         { ALT_C, ClearScreen },     { ALT_E, ToggleEcho },
  288.         { ALT_S, DOS_Shell },       { ALT_H, HangUp },
  289.         { ALT_L, ToggleLineFeeds }, { F2, ToggleLogFile },
  290.         { ALT_P, ChangeParameters },{ F4, DOS_Command },
  291.         { F3, MessageUpload },      { F5, DSZUpload },
  292.         { F6, DSZDownload },        { 0, NULL } },
  293.       *JumpTableList[] = { TERMINAL_Table, HOSTMODE_Table };
  294.  
  295.     JUMP_TABLE *pjt = JumpTableList[Mode];
  296.  
  297.     if (LongCh == F1)
  298.         LongCh = DisplayHelp();         // run Help if F1 was pressed
  299.     while (pjt->KeyCode != LongCh && pjt->KeyCode)
  300.         ++pjt;                          // look up the key in the table
  301.     if (pjt->KeyCode)
  302.         pjt->Function();                // if key found, run the function
  303. }
  304.  
  305. /////////////////////////////////////////////////
  306. //  ProcessAsyncChar                           //
  307. //://////////////////////////////////////////////
  308. int ProcessAsyncChar()
  309. {
  310.     static int ToggleHost = 0;
  311.  
  312.     char Ch = (char)port.Rx();          // read the character
  313.     if (Ch == '\r' && LineFeeds)
  314.         Ch = '\n';                      // translate to LF if supplying LF's
  315.     if (Ch != '~')                      // using ~ to remotely toggle host
  316.         ToggleHost = 0;                 //  mode, reset counter if not ~
  317.     else
  318.     {
  319.         if (++ToggleHost >= 10)         // seq of 10 ~'s toggles host mode
  320.         {
  321.             HostMode();
  322.             ToggleHost = 0;             // reset counter on HostMode exit
  323.             return 0;
  324.         }
  325.     }
  326.     Display(Ch);                        // display the character
  327.     if (LogFile && v_ansiseq == '\0')
  328.         fputc(Ch, LogFile);             // write to log file if its on
  329.     return ((int)Ch & 0xff);            // return received char
  330. }
  331.  
  332. /////////////////////////////////////////////////
  333. //  SaveScreen                                 //
  334. //://////////////////////////////////////////////
  335. int SaveScreen()
  336. {
  337.     if (PushScreen(0, 0, 25, 80) != 0)  // move screen to memory buffer
  338.         return 0;                       //  return failed if no memory
  339.     VideoStatus(1);                     // save video color & ANSI status
  340.     SetWindowSize(0, 0, 24, 79);
  341.     v_color = wht;
  342.     ClearWindow();                      // reset window size, clear screen
  343.     return 1;                           // return success
  344. }
  345.  
  346. /////////////////////////////////////////////////
  347. //  RestoreScreen                              //
  348. //://////////////////////////////////////////////
  349. void RestoreScreen()
  350. {
  351.     PopScreen();                        // restore screen
  352.     VideoStatus(0);                     // restore v_color & v_ansiseq
  353.     SetWindowSize(0, 0, 23, 79);        // reset the window size
  354. }
  355.  
  356. /////////////////////////////////////////////////
  357. //  VideoStatus                                //
  358. //://////////////////////////////////////////////
  359. void VideoStatus(int saving)
  360. {
  361.     static char savedcolor[4], savedseq[4], vsi = 0;
  362.  
  363.     if (saving)
  364.         savedcolor[vsi] = v_color, savedseq[vsi++] = v_ansiseq,
  365.         v_ansiseq = '\0';
  366.     else
  367.         v_color = savedcolor[--vsi], v_ansiseq = savedseq[vsi];
  368. }
  369.  
  370. /////////////////////////////////////////////////
  371. //  ClearScreen                                //
  372. //://////////////////////////////////////////////
  373. void ClearScreen()
  374. {
  375.     v_color = cyn;                      // reset screen color
  376.     SetWindowSize(0, 0, 23, 79);        // reset the window size
  377.     ClearWindow();                      // clear the current window
  378. }
  379.  
  380. /////////////////////////////////////////////////
  381. //  ToggleLogFile                              //
  382. //://////////////////////////////////////////////
  383. void ToggleLogFile()
  384. {
  385.     if (LogFile == NULL)                // if it's off, turn it on
  386.     {
  387.         if ((LogFile = fopen("ST.LOG", "ab")) != NULL)
  388.             Display(24, 25, hgrn, "LOG");
  389.     }
  390.     else                                // if it's on, turn it off
  391.     {
  392.         fclose(LogFile);
  393.         LogFile = NULL;                 // a NULL FILE * shows log is off
  394.         Display(24, 25, cyn, "═══");
  395.     }
  396. }
  397.  
  398. /////////////////////////////////////////////////
  399. //  ToggleEcho                                 //
  400. //://////////////////////////////////////////////
  401. void ToggleEcho()
  402. {
  403.     Echo ^= 1;                          // switch state, update status line
  404.     Display(24, 15, (Echo) ? hgrn : cyn, (Echo) ? "ECHO" : "════");
  405. }
  406.  
  407. /////////////////////////////////////////////////
  408. //  ToggleLineFeeds                            //
  409. //://////////////////////////////////////////////
  410. void ToggleLineFeeds()
  411. {
  412.     LineFeeds ^= 1;                     // switch state, update status line
  413.     Display(24, 21, (LineFeeds) ? hgrn : cyn, (LineFeeds) ? "LF" : "══");
  414. }
  415.  
  416. /////////////////////////////////////////////////
  417. //  ToggleDtr                                  //
  418. //://////////////////////////////////////////////
  419. void ToggleDtr()
  420. {
  421.     Dtr ^= 1;                           // switch state, update status line
  422.     port.Dtr(Dtr);
  423.     Display(24, 10, (Dtr) ? hgrn : (hred | BLNK), "DTR");
  424. }
  425.  
  426. /////////////////////////////////////////////////
  427. //  DialNumber                                 //
  428. //://////////////////////////////////////////////
  429. void DialNumber()
  430. {
  431.     char Response[25], *pc = Response;
  432.  
  433.     if (Prompt(pc, "Enter number (p###=Pulse, r=Redial): ", 24) != 0)
  434.         return;                         // get number, exit if ESC pressed
  435.     if (isalpha(*pc))                   // look for 'P' or 'R'
  436.     {
  437.         *pc = toupper(*pc);
  438.         if (*pc == 'R' && *DialString != '\0')
  439.         {
  440.             port.Tx(DialString);        // redial command
  441.             return;
  442.         }
  443.         else if (*pc != 'P')
  444.             return;                     // invalid response
  445.     }
  446.     if (*pc == 'P')
  447.         sprintf(DialString, "%s%s\r", "ATDP", &pc[1]);
  448.     else
  449.         sprintf(DialString, "%s%s\r", "ATDT", pc);
  450.     port.Tx(DialString);
  451. }
  452.  
  453. /////////////////////////////////////////////////
  454. //  DOS_Shell                                  //
  455. //://////////////////////////////////////////////
  456. void DOS_Shell()
  457. {
  458.     char *comspec;
  459.  
  460.     comspec = getenv("COMSPEC");        // get command interpreter name
  461.     if (comspec == NULL)
  462.         comspec = "COMMAND.COM";
  463.     if (SaveScreen() == 0)              // save the current screen
  464.         return;                         //  exit if save failed
  465.     Display(hred, "Type EXIT to return to SMALTERM.");
  466.     spawnlp(0, comspec, comspec, NULL); // run command interpreter
  467.     RestoreScreen();                    // restore the original screen
  468. }
  469.  
  470. /////////////////////////////////////////////////
  471. //  DOS_Command                                //
  472. //://////////////////////////////////////////////
  473. void DOS_Command()
  474. {
  475.     char buf[48];
  476.  
  477.     if (Prompt(buf, "Enter DOS command: ", 46) != 0)
  478.         return;                         // get the command
  479.     if (SaveScreen() == 0)              // save the current screen
  480.         return;                         //  exit if save failed
  481.     system(buf);                        // execute command
  482.     RestoreScreen();                    // restore the original screen
  483. }
  484.  
  485. /////////////////////////////////////////////////
  486. //  HostMode                                   //
  487. //://////////////////////////////////////////////
  488. void HostMode()
  489. {
  490.     // do this later
  491. }
  492.  
  493. /////////////////////////////////////////////////
  494. //  MessageUpload                              //
  495. //://////////////////////////////////////////////
  496. void MessageUpload()
  497. {
  498.     char buf[81];
  499.     FILE *fh;
  500.     char *pc;
  501.  
  502.     if (Prompt(buf, "Enter ASCII filename: ", 46) != 0)
  503.         return;                         // get the file name
  504.     if ((fh = fopen(buf, "rt")) == NULL)
  505.     {
  506.         Prompt(buf, "File open error, Press ENTER\a", 0);
  507.         return;                         // abort if can't open the file
  508.     }
  509.     port.RxFlush();                     // flush the port
  510.     port.TxFlush();
  511.     while (fgets(buf, 80, fh))          // while more is left
  512.     {
  513.         if (*buf == '\n')               // change blank lines to SPC,CRs
  514.             strcpy(buf, " \r");
  515.         else if ((pc = strchr(buf, '\n')) != NULL)
  516.             *pc = '\r';                 // change LFs to simulate ENTER key
  517.         for (pc = buf; *pc; pc++)
  518.         {
  519.             while (!port.TxEmpty())     // send a char at a time for looks
  520.             {
  521.                 if (KBHIT && (KBREAD == X_ESC))
  522.                 {                       // monitor for upload aborted
  523.                     fclose(fh);
  524.                     return;
  525.                 }
  526.                 while (port.RxLevel() != 0)
  527.                     ProcessAsyncChar();
  528.             }
  529.             port.Tx(*pc);
  530.         }
  531.     }
  532.     fclose(fh);                         // close the file
  533. }
  534.  
  535. /////////////////////////////////////////////////
  536. //  DSZUpload                                  //
  537. //://////////////////////////////////////////////
  538. void DSZUpload()
  539. {
  540.     RunBatchFile("SND.BAT");
  541. }
  542.  
  543. /////////////////////////////////////////////////
  544. //  DSZDownload                                //
  545. //://////////////////////////////////////////////
  546. void DSZDownload()
  547. {
  548.     RunBatchFile("RCV.BAT");
  549. }
  550.  
  551. /////////////////////////////////////////////////
  552. //  RunBatchFile                               //
  553. //://////////////////////////////////////////////
  554. void RunBatchFile(char *FileName)
  555. {
  556.     char lbuf[50], *pc = lbuf;
  557.  
  558.     strcpy(pc, FileName);
  559.     pc += strlen(pc);
  560.     *pc++ = ' ';                        // copy filename to local buffer
  561.     if (Prompt(pc, "Enter batch parameters -> ", 40) == ESC_PRESSED)
  562.         return;                         // add on any args, return if ESC
  563.     if (SaveScreen() == 0)              // save current screen
  564.         return;                         //  return if save failed
  565.     vDisplay(hred, "Executing: %s\n\n", lbuf);
  566.     system(lbuf);                       // run the batch file
  567.     RestoreScreen();                    // restore the screen
  568. }
  569.  
  570. /////////////////////////////////////////////////
  571. //  SendFile                                   //
  572. //://////////////////////////////////////////////
  573. void SendFile()
  574. {
  575.     char buf[48], *pc;
  576.  
  577.     if (Prompt(buf, "Enter Filename{,k}: ", 46) != 0)
  578.         return;                         // get the file name
  579.     pc = strchr(buf, ',');              // check if Xmodem-1k selected
  580.     if (pc && (strchr(pc, 'k') || strchr(pc, 'K')))
  581.     {
  582.         *pc = '\0';
  583.         SendXmodem(buf, 1024);
  584.     }
  585.     else                                // send the file
  586.         SendXmodem(buf);
  587. }
  588.  
  589. /////////////////////////////////////////////////
  590. //  ReceiveFile                                //
  591. //://////////////////////////////////////////////
  592. void ReceiveFile()
  593. {
  594.     char buf[48], *pc;
  595.  
  596.     if (Prompt(buf, "Enter Filename: ", 46) != 0)
  597.         return;                         // get the file name
  598.     ReceiveXmodem(buf);                 // receive the file
  599. }
  600.  
  601. /////////////////////////////////////////////////
  602. //  ExitProgram                                //
  603. //://////////////////////////////////////////////
  604. void ExitProgram()
  605. {
  606.     char buf[2];
  607.  
  608.     switch(Prompt(buf, "Exit SmalTerm (Y, n)? ", 1))
  609.     {                                   // make sure they want to do this
  610.         case ESC_PRESSED:
  611.             return;                     // return if ESC
  612.         default:
  613.             if (*buf != 'y' && *buf != 'Y')
  614.                 return;                 // if not 'Yes', then return
  615.         case NO_INPUT:                  // 'Enter key' is a default 'yes'
  616.             break;
  617.     }
  618.     RestoreScreen();                    // replace the original screen
  619.     tickhookset(0);                     // remove the interrupt 1C hook
  620.     exit(0);                            // leave the program
  621. }
  622.  
  623. /////////////////////////////////////////////////
  624. //  DisplayHelp                                //
  625. //://////////////////////////////////////////////
  626. int DisplayHelp()               // interim help function
  627. {
  628.     int LongCh;
  629.     static char *HelpScrn = "\
  630.     F1 - Help                   ALT C - Clear Screen\n\
  631.     F2 - Toggle Log File        ALT D - Dial Number\n\
  632.     F3 - BBS Message Upload     ALT E - Toggle Echo\n\
  633.     F4 - Execute DOS Command    ALT H - Hang Up\n\
  634.     F5 - DSZ Upload   SND.BAT   ALT L - Toggle Line Feeds\n\
  635.     F6 - DSZ Download RCV.BAT   ALT P - Change Parameters\n\
  636.    F10 - Host Mode (not there)  ALT S - DOS Shell\n\
  637.   PGUP - Xmodem Send            ALT X - Exit Program\n\
  638.   PGDN - Xmodem Receive  << Press ESC or Command >>";
  639.  
  640.     DrawBox(5, 10, 12, 60);
  641.     SetWindowSize(6, 11, 18, 69);
  642.     v_color = ylw;
  643.     Display(6, 12, "SMALTERM COMMANDS:\n");
  644.     Display(wht, HelpScrn);
  645.     LongCh = KBREAD;
  646.     RemoveBox();
  647.     SetWindowSize(0, 0, 23, 79);
  648.     return LongCh;
  649. }
  650.  
  651. /////////////////////////////////////////////////
  652. //  DisplayParameters                          //
  653. //://////////////////////////////////////////////
  654. void DisplayParameters()
  655. {
  656.     Fill(24, 65, '═', cyn, 10);
  657.     Display(24, 65, hgrn, port.Params());
  658. }
  659.  
  660. /////////////////////////////////////////////////
  661. //  ChangeParameters                           //
  662. //://////////////////////////////////////////////
  663. void ChangeParameters()
  664. {
  665.     char buf[12];
  666.  
  667.     if (Prompt(buf, "Enter parameters (Ex: 1200N81) -> ", 10) != 0)
  668.         return;                         // get the new parameters
  669.     if (port.Params(strupr(buf)) == 0)
  670.         DisplayParameters();            // display updated params if success,
  671.     else                                //  else display error message
  672.         Prompt(buf, "Invalid parameters, Press ENTER to continue\a", 0);
  673. }
  674.  
  675. /////////////////////////////////////////////////
  676. //  DrawBox                                    //
  677. //://////////////////////////////////////////////
  678. int DrawBox(int Top, int Lft, int Height, int Length)
  679. {
  680.     static char BoxChar[] = { '╔', '╗', '╚', '╝', '═', '║' };
  681.     int Btm, Rgt;
  682.  
  683.     if (PushScreen(Top, Lft, Height, Length) != 0)
  684.         return 0;                   // save area for box, return if failed
  685.     Btm = Top + Height - 1;
  686.     Rgt = Lft + Length - 1;
  687.     VideoStatus(1);                 // save video variables
  688.     v_color = wht;
  689.     SetWindowSize(Top, Lft, Btm, Rgt);
  690.     ClearWindow();                  // clear the area for the box
  691.     SetWindowSize(0, 0, 23, 79);
  692.     v_color = hblu;                 // draw the box
  693.     Fill(Top, Lft, BoxChar[4], hblu, Length);
  694.     Fill(Btm, Lft, BoxChar[4], hblu, Length);
  695.     Fill(Top, Rgt, BoxChar[5], hblu, Height, 0);
  696.     Fill(Top, Lft, BoxChar[5], hblu, Height, 0);
  697.     Display(Top, Lft, BoxChar[0]);
  698.     Display(Top, Rgt, BoxChar[1]);
  699.     Display(Btm, Lft, BoxChar[2]);
  700.     Display(Btm, Rgt, BoxChar[3]);
  701.     return 1;
  702. }
  703.  
  704. /////////////////////////////////////////////////
  705. //  RemoveBox                                  //
  706. //://////////////////////////////////////////////
  707. void RemoveBox()
  708. {
  709.     PopScreen();                        // restore screen
  710.     VideoStatus(0);                     // reset v_color & v_ansiseq
  711. }
  712.  
  713. /////////////////////////////////////////////////
  714. //  Prompt                                     //
  715. //://////////////////////////////////////////////
  716. int Prompt(char *Response, char *PromptMsg, int MaxResponse)
  717. {
  718.     int Lft, Length;
  719.     char *pc, *end, ch;
  720.  
  721.     Length = strlen(PromptMsg) + MaxResponse + 6;
  722.     Lft = (80 - Length) / 2;            // calculate box size required
  723.     if (DrawBox(9, Lft, 5, Length) == 0) // draw the box
  724.         return ESC_PRESSED;
  725.     v_color = hred;
  726.     Display(11, Lft + 3, PromptMsg);    // display prompt
  727.     v_color = wht;
  728.     pc = Response, end = pc + MaxResponse;
  729.  
  730.     while ((ch = (char)KBREAD) != '\r' && ch != ESC)
  731.     {                                   // get the response
  732.         if (ch == '\b')
  733.         {                               // backspace key
  734.             if (pc > Response)
  735.                 --pc, Display(ch);
  736.             continue;
  737.         }
  738.         if (pc != end && isprint((int)ch & 0xff))
  739.         {                               // good character
  740.             Display(ch), *pc++ = ch;
  741.             continue;
  742.         }
  743.         Display('\a');                  // illegal character
  744.     }
  745.     *pc = '\0';                         // terminate the string
  746.     RemoveBox();
  747.     if (ch == ESC)
  748.         return ESC_PRESSED;             // return something
  749.     if (*Response == '\0')
  750.         return NO_INPUT;
  751.     return 0;
  752. }
  753.  
  754. /////////////////////////////////////////////////
  755. //  HangUp                                     //
  756. //://////////////////////////////////////////////
  757. void HangUp()
  758. {
  759.     ToggleDtr();                        // drop DTR
  760.     port.TxFlush();
  761.     port.RxFlush();                     // flush the buffers
  762.     if (MonitoredDelay(24, 1) == 0)     // wait a while for carrier
  763.     {                                   //  to drop
  764.         port.Tx("+++");                 // if dropping Dtr didn't
  765.         if (MonitoredDelay(18, 1) == 0) //  work try modem command
  766.         {
  767.             port.Tx("ATH0\r");
  768.             WaitFor("OK", 54, 1);       // 3 secs, monitor carrier
  769.         }
  770.     }
  771.     ToggleDtr();
  772. }
  773.  
  774. /////////////////////////////////////////////////
  775. //  MonitoredDelay                             //
  776. //://////////////////////////////////////////////
  777. int MonitoredDelay(int Ticks, int MonitorCarrier)
  778. {
  779.     int LongCh;
  780.  
  781.     Timer t(Ticks);                     // start the delay
  782.     while (!t.Expired())
  783.     {
  784.         if ((LongCh = KBHIT) != 0)      // check the keyboard
  785.         {
  786.             if (LongCh == X_ESC)
  787.             {
  788.                 KBREAD;
  789.                 return ESC_PRESSED;     // return if ESC pressed
  790.             }
  791.             ProcessKeyPress();          // process key if not ESC
  792.         }
  793.         if (MonitorCarrier && !port.Carrier())
  794.             return NO_CARRIER;
  795.         if (port.RxLevel() != 0)        // check the async port
  796.             ProcessAsyncChar();
  797.     }
  798.     return 0;
  799. }
  800.  
  801. /////////////////////////////////////////////////
  802. //  WaitFor                                    //
  803. //://////////////////////////////////////////////
  804. int WaitFor(char *String, int Ticks, int MonitorCarrier)
  805. {
  806.     char ch, *pc, *buf, *end;
  807.     int MatchLen, LongCh, rval;
  808.  
  809.     if ((MatchLen = strlen(String)) == 0)
  810.         return (0);                     // return if nothing to wait for
  811.     buf = new char[MatchLen];           // allocate & clear incoming chars buf
  812.     memset(buf, '\0', MatchLen);
  813.     pc = buf - 1;
  814.     end = pc + MatchLen;                // preset pointers
  815.  
  816.     Timer t(Ticks);                     // start a timer for 'Ticks' duration
  817.     while (1)
  818.     {
  819.         while (port.RxLevel() != 0)     // check for characters in the port
  820.         {
  821.             ch = ProcessAsyncChar();
  822.             if (pc != end)
  823.             {                           // if don't have MatchLen chars yet
  824.                 *++pc = ch;
  825.                 if (pc != end)
  826.                     continue;
  827.             }
  828.             else                        // have MatchLen chars
  829.             {
  830.                 memmove(buf, &buf[1], MatchLen);
  831.                 *pc = ch;               // put this char in the match buffer
  832.             }                           //  and check for a match
  833.             if (*buf == *String && !memicmp(buf, String, MatchLen))
  834.             {
  835.                 delete buf;             // if found wait for string
  836.                 return 0;
  837.             }
  838.         }
  839.         if ((LongCh = KBHIT) != 0)      // if got a keyboard entry
  840.         {
  841.             if (LongCh == X_ESC)
  842.             {
  843.                 KBREAD;
  844.                 rval = ESC_PRESSED;     // return if it was ESC
  845.                 break;
  846.             }
  847.             ProcessKeyPress();          //  else process key
  848.         }
  849.         if (t.Expired())                // if max wait time has elapsed
  850.         {
  851.             rval = ESC_PRESSED;
  852.             break;
  853.         }
  854.         if (MonitorCarrier && !port.Carrier())
  855.         {                               // if lost carrier & care
  856.             rval = NO_CARRIER;
  857.             break;
  858.         }
  859.     }
  860.     delete buf;
  861.     return rval;
  862. }
  863.  
  864. /////////////////////////////////////////////////
  865. //  vDisplay (1 of 3)                          //
  866. //://////////////////////////////////////////////
  867. void vDisplay(char *Format, ...)
  868. {
  869.     va_list arg_ptr;
  870.     va_start(arg_ptr, Format);
  871.     vsprintf(vDispBuf, Format, arg_ptr);
  872.     Display(vDispBuf);
  873. }
  874.  
  875. /////////////////////////////////////////////////
  876. //  vDisplay (2 of 3)                          //
  877. //://////////////////////////////////////////////
  878. void vDisplay(int Color, char *Format, ...)
  879. {
  880.     va_list arg_ptr;
  881.     va_start(arg_ptr, Format);
  882.     vsprintf(vDispBuf, Format, arg_ptr);
  883.     Display(Color, vDispBuf);
  884. }
  885.  
  886. /////////////////////////////////////////////////
  887. //  vDisplay (3 of 3)                          //
  888. //://////////////////////////////////////////////
  889. void vDisplay(int Row, int Col, char *Format, ...)
  890. {
  891.     va_list arg_ptr;
  892.     va_start(arg_ptr, Format);
  893.     vsprintf(vDispBuf, Format, arg_ptr);
  894.     Display(Row, Col, vDispBuf);
  895. }
  896.  
  897. /////////////////////////////////////////////////
  898. //  PushScreen                                 //
  899. //://////////////////////////////////////////////
  900. int PushScreen(int Row, int Col, int NbrRows, int NbrCols)
  901. {
  902.     if (ScreenIndex == 10)              // return if ScreensArray is full
  903.         return (-1);
  904.     if ((ScreensArray[ScreenIndex] = new char[2*NbrRows*NbrCols+16]) == NULL)
  905.         return (-2);                    // return if can't alloc screen buffer
  906.     pu_scrnd(Row, Col, NbrRows, NbrCols, ScreensArray[ScreenIndex]);
  907.     ++ScreenIndex;                      // increment the index
  908.     return 0;
  909. }
  910.  
  911. /////////////////////////////////////////////////
  912. //  PopScreen                                  //
  913. //://////////////////////////////////////////////
  914. int PopScreen()
  915. {
  916.     if (ScreenIndex == 0)
  917.         return (-1);                    // return if no screens pushed
  918.     --ScreenIndex;
  919.     po_scrnd(ScreensArray[ScreenIndex]); // call low level restore screen
  920.     delete ScreensArray[ScreenIndex];    // release allocated memory
  921.     return 0;
  922. }
  923.  
  924.  
  925. /////////////////////////////////////////////////
  926. //  ReceiveXmodem                              //
  927. //://////////////////////////////////////////////
  928. void ReceiveXmodem(char *FileName)
  929. {
  930.     FILE *fh;
  931.     char *pc, *buf, *end;
  932.  
  933.     /////////////////////////////////////////////
  934.     //  alloc memory, open file, display info  //
  935.     /////////////////////////////////////////////
  936.     if ((buf = new char[1024 + 10]) == NULL) // alloc enough for 1K blocks
  937.     {
  938.         Prompt((char *)&buf, "Not enough memory, Press ENTER\a", 0);
  939.         return;                         // abort if can't alloc memory
  940.     }
  941.     if ((fh = fopen(FileName, "rb")) != NULL)
  942.     {
  943.         Prompt((char *)&buf, "File exists, Press ENTER\a", 0);
  944.         delete buf;
  945.         return;                         // abort if file exists
  946.     }
  947.     if ((fh = fopen(FileName, "wb")) == NULL)
  948.     {
  949.         Prompt((char *)&buf, "File creation error, Press ENTER\a", 0);
  950.         delete buf;
  951.         return;                         // abort if can't open output file
  952.     }
  953.  
  954.     ///////////////////////////////
  955.     //  pull up transfer window  //
  956.     ///////////////////////////////
  957.     TransferWindow(-1);                 // display receive transfer window
  958.     Display(1, 48, strupr(FileName));
  959.  
  960.     //////////////////////////////////////////////////////
  961.     //  set xflow off, params to N81, ignore bad chars  //
  962.     //////////////////////////////////////////////////////
  963.     char save_xoff, save_flushbad, save_params[10];
  964.     SetXmodemMode(save_xoff, save_flushbad, save_params);
  965.  
  966.     //////////////////////////////
  967.     //  intitiate the transfer  //
  968.     //////////////////////////////
  969.     static int BlockSizeAry[] = { 0, 128, 1024 };  // nul, SOH, STX
  970.     int rval, LongCh, CANsRxd, TimeOuts, BlockSize, UseCrc;
  971.     int i, BlockNbr, RxdBlockNbr, Checksum, NeedHdrChar, ConsecErrors;
  972.     long BytesTransferred = 0L;
  973.  
  974.     TimeOuts = CANsRxd = 0;
  975.     rval = 1;
  976.     while (rval == 1)
  977.     {
  978.         port.Tx((TimeOuts < 6) ? 'C' : NAK);    // try CRC Xmodem first, if
  979.         switch (LongCh = GetHdrChar(90))        // no response try checksum
  980.         {
  981.           case SOH:                     // 128 byte block header
  982.           case STX:                     // 1K byte block header
  983.             BlockSize = BlockSizeAry[LongCh];
  984.             rval = 0;
  985.             break;
  986.           case TIMED_OUT:               // no response
  987.             if (++TimeOuts > 10)
  988.                 rval = TIMED_OUT;
  989.             break;
  990.           default:                      // fatal error
  991.             rval = LongCh;
  992.             break;
  993.         }
  994.     }
  995.     UseCrc = (TimeOuts < 5) ? 1 : 0;    // set type of checksum to use
  996.     BlockNbr = 1;                       // initial block number
  997.     NeedHdrChar = TimeOuts = ConsecErrors = 0;
  998.  
  999.     while (!rval)
  1000.     {
  1001.         LongCh = 0;
  1002.         while (!LongCh)                 // while good blocks
  1003.         {
  1004.             ////////////////////////////////
  1005.             //  get the next header char  //
  1006.             ////////////////////////////////
  1007.             if (NeedHdrChar)            // if haven't already got SOH
  1008.             {
  1009.                 LongCh = GetHdrChar(180);   // wait up to 10 secs
  1010.                 if ((char)LongCh != SOH && (char)LongCh != STX)
  1011.                     break;
  1012.                 TimeOuts = 0;
  1013.                 BlockSize = BlockSizeAry[LongCh];
  1014.             }
  1015.             else
  1016.                 NeedHdrChar = 1;
  1017.             ////////////////////////////
  1018.             //  get the block number  //
  1019.             ////////////////////////////
  1020.             if ((LongCh = RxWithTimeOut(90)) < 0)   // 90 == 5 secs
  1021.                 break;
  1022.             RxdBlockNbr = LongCh;
  1023.             if ((LongCh = RxWithTimeOut(90)) < 0)
  1024.                 break;
  1025.             if (LongCh + RxdBlockNbr != 0xff)
  1026.             {                               // block complement error
  1027.                 LongCh = BLK_COMP_ERR;
  1028.                 break;
  1029.             }
  1030.             if ((char)RxdBlockNbr != (char)BlockNbr)
  1031.             {                               // not expected block number
  1032.                 LongCh = BLK_SEQ_ERR;
  1033.                 break;
  1034.             }
  1035.             ////////////////////////
  1036.             //  receive the data  //
  1037.             ////////////////////////
  1038.             Checksum = 0;
  1039.             pc = buf, end = pc + BlockSize; // initialize variables
  1040.             while (pc < end)
  1041.             {
  1042.                 if ((LongCh = RxWithTimeOut(90)) < 0)
  1043.                     break;
  1044.                 *pc++ = (char)LongCh;       // read and store data
  1045.                 if (UseCrc)                 // update crc if using crc
  1046.                     Checksum = update_crc(Checksum, (char)LongCh);
  1047.                 else
  1048.                     Checksum += LongCh;     // else update simple checksum
  1049.             }
  1050.             if (pc != end)                  // if an error occurred while
  1051.                 break;                      //  receiving block data
  1052.             ///////////////////////////////
  1053.             //  receive the check value  //
  1054.             ///////////////////////////////
  1055.             if ((LongCh = RxWithTimeOut(90)) < 0)
  1056.                 break;                  // high byte of crc or the checksum
  1057.             if (UseCrc)
  1058.             {
  1059.                 if ((Checksum ^= (LongCh << 8)) & 0xff00)
  1060.                 {                          // crc didn't match, breaking here
  1061.                     LongCh = CHECKSUM_ERR; // makes error recovery faster on
  1062.                     break;                 // short blocks
  1063.                 }
  1064.                 if ((LongCh = RxWithTimeOut(90)) < 0)
  1065.                     break;              // low byte of crc
  1066.             }
  1067.             if ((LongCh ^= Checksum) != 0)
  1068.             {                           // crc or checksum didn't match
  1069.                 LongCh = CHECKSUM_ERR;
  1070.                 break;
  1071.             }
  1072.             port.Tx(ACK);                   // good block, send an ACK
  1073.             fwrite(buf, 1, BlockSize, fh);  // update the file
  1074.             ++BlockNbr;                     // advance the blocknbr
  1075.             BytesTransferred += (long)BlockSize;
  1076.             vDisplay(3, 48, "%ld", BytesTransferred);
  1077.             ConsecErrors = 0;               // reset consecutive error count
  1078.         }
  1079.         ///////////////////////////////
  1080.         //  errors and EOT handling  //
  1081.         ///////////////////////////////
  1082.         if (LongCh == EOT)                  // end of file indicator
  1083.         {
  1084.             for (i = 0; i < 3; i++)
  1085.             {
  1086.                 port.Tx(NAK);               // try to confirm real EOT
  1087.                 if ((char)(LongCh = GetHdrChar(90)) == EOT)
  1088.                 {
  1089.                     port.Tx(ACK);           // if got 2nd EOT after NAKing
  1090.                     rval = 1;               // 1st one, it must be real EOT
  1091.                     break;
  1092.                 }
  1093.                 if ((char)LongCh == SOH     // if got an SOH or STX, 1st EOT
  1094.                  || (char)LongCh == STX     // must have been false
  1095.                  || (LongCh < 0 && LongCh != TIMED_OUT))
  1096.                     break;                  // if < 0, fatal error occurred
  1097.             }
  1098.             if (LongCh == TIMED_OUT)        // got an EOT but after 3 attempts
  1099.                 rval = 2;                   // to get confirmation, timed out,
  1100.             if (rval)                       // so exit at different level
  1101.                 break;
  1102.  
  1103.             if ((char)LongCh == SOH || (char)LongCh == STX)
  1104.             {                                       // got EOT followed by
  1105.                 Display(5, 48, "False EOT");        // valid start of block
  1106.                 BlockSize = BlockSizeAry[LongCh];   // char, try to get
  1107.                 NeedHdrChar = 0;                    // another block
  1108.                 continue;
  1109.             }
  1110.         }
  1111.         if (++ConsecErrors == 10)       // exit after 10 errors
  1112.             LongCh = ERROR_LIMIT;
  1113.         vDisplay(4, 48, "%d", ConsecErrors);
  1114.         switch (LongCh)                 // switch on error code
  1115.         {
  1116.           case BLK_COMP_ERR:
  1117.             Display(5, 48, "Bad Block Complement");   //  complement error
  1118.             if ((rval = WaitForBlockToEnd(18)) != 0)  // wait for port to dry
  1119.                 break;                                // up, send a NAK, and
  1120.             port.Tx(NAK);                             // and try again
  1121.             break;
  1122.           case BLK_SEQ_ERR:
  1123.             Display("Bad Block Sequence  ");
  1124.             if ((char)RxdBlockNbr != (char)(BlockNbr - 1)
  1125.              || (LongCh = WaitForBlockToEnd(18)) != 0)
  1126.             {                                         //  sequence error
  1127.                 rval = LongCh;                        // if wrong seq and not
  1128.                 break;                                // previous seq either,
  1129.             }                                         // then have fatal error.
  1130.             port.Tx(ACK);                             // if got repeat block,
  1131.             continue;                                 // ACK it and try again
  1132.           case CHECKSUM_ERR:
  1133.             Display(5, 48, (UseCrc) ?                 //  bad crc or checksum
  1134.              "Block Crc Error     " :
  1135.              "Block Checksum Error");
  1136.             if ((rval = WaitForBlockToEnd(4)) != 0)   // wait for about 1/4
  1137.                 break;                                // second of silence,
  1138.             port.Tx(NAK);                             // then NAK block and
  1139.             break;                                    // try again
  1140.           case TIMED_OUT:
  1141.             Display(5, 48,                  //  remote not answering
  1142.              "Timed Out           ");
  1143.             port.Tx(NAK);                   // if no answer, send a NAK to
  1144.             break;                          // try to get a response
  1145.           default:
  1146.             rval = LongCh;                  //   CAN, ESC, lost carrier, ...
  1147.             break;                          // set exit code and quit
  1148.         }
  1149.     }
  1150.     if (rval == ESC_PRESSED)                // send CANs if ESC pressed
  1151.     {
  1152.         Display(5, 48, "Aborting File Transfer ...");
  1153.         WaitForBlockToEnd(18);
  1154.         SendCANs();
  1155.     }
  1156.     fclose(fh);                             // close the file, free the
  1157.     delete(buf);                            // memory, reset params and exit
  1158.     ResetXmodemMode(save_xoff, save_flushbad, save_params);
  1159.     TransferWindow(0);
  1160.     XmodemExitMsg(rval);                    // display exit message
  1161. }
  1162.  
  1163. /////////////////////////////////////////////////
  1164. //  SendXmodem                                 //
  1165. //://////////////////////////////////////////////
  1166. void SendXmodem(char *FileName, int BlockSize)
  1167. {
  1168.     FILE *fh;
  1169.     char *pc, *buf, *end;
  1170.     long FileLength;
  1171.  
  1172.     /////////////////////////////////////////////
  1173.     //  alloc memory, open file, display info  //
  1174.     /////////////////////////////////////////////
  1175. #if !defined(__TURBOC__)
  1176.     FileLength = filesize(FileName);    // Zortech's filesize
  1177. #endif
  1178.     if ((buf = new char[BlockSize + 10]) == NULL)
  1179.     {
  1180.         Prompt((char *)&buf, "Not enough memory, Press ENTER\a", 0);
  1181.         return;                         // abort if can't alloc memory
  1182.     }
  1183.     if ((fh = fopen(FileName, "rb")) == NULL)
  1184.     {
  1185.         Prompt((char *)&buf, "File not found, Press ENTER\a", 0);
  1186.         delete buf;
  1187.         return;                         // abort if can't open file
  1188.     }
  1189. #if defined(__TURBOC__)
  1190.     FileLength = filelength(fileno(fh));
  1191. #endif
  1192.  
  1193.     ///////////////////////////////
  1194.     //  pull up transfer window  //
  1195.     ///////////////////////////////
  1196.     TransferWindow(1);                  // display transmit transfer window
  1197.     Display(1, 48, strupr(FileName));
  1198.     vDisplay(2, 48, "%ld", FileLength); // display filename and size
  1199.  
  1200.     //////////////////////////////////////////////////////
  1201.     //  set xflow off, params to N81, ignore bad chars  //
  1202.     //////////////////////////////////////////////////////
  1203.     char save_xoff, save_flushbad, save_params[10];
  1204.     SetXmodemMode(save_xoff, save_flushbad, save_params);
  1205.  
  1206.     /////////////////////////////
  1207.     //  wait for a 'C' or NAK  //
  1208.     /////////////////////////////
  1209.     int LongCh, rval, CANsRxd, TimeOuts, UseCrc;
  1210.  
  1211.     CANsRxd = TimeOuts = UseCrc = 0;
  1212.     rval = 1;
  1213.     while (rval == 1)                   // wait for something to happen
  1214.     {
  1215.         switch (LongCh = RxWithTimeOut(18))
  1216.         {
  1217.           case 'C':                     // CRC xmodem
  1218.               UseCrc = 1;
  1219.               rval = 0;
  1220.               break;
  1221.           case NAK:                     // simple checksum xmodem
  1222.               rval = 0;
  1223.               break;
  1224.           case TIMED_OUT:               // didn't get anything
  1225.               if (++TimeOuts < 60)
  1226.                   continue;
  1227.               rval = LongCh;
  1228.               break;
  1229.           case CAN:                     // remote sent control-X's
  1230.               if (++CANsRxd < 2)
  1231.                   continue;
  1232.                 rval = GOT_CANNED;
  1233.                 break;
  1234.           case NO_CARRIER:              // lost the carrier
  1235.           case ESC_PRESSED:             // cancelled on this end
  1236.               rval = LongCh;
  1237.               break;
  1238.           default:
  1239.               CANsRxd = 0;
  1240.               break;
  1241.         }
  1242.     }
  1243.  
  1244.     ////////////////////////////
  1245.     //  send the file blocks  //
  1246.     ////////////////////////////
  1247.     char BlockNbr = '\0', HdrChar = (BlockSize == 1024) ? STX : SOH;
  1248.     int i, ConsecErrors, BadBlock, FirstAck, Checksum, ExtraNAK;
  1249.     long BytesTransferred = 0L;
  1250.  
  1251.     BadBlock = FirstAck = CANsRxd = 0;
  1252.     while (!rval)
  1253.     {
  1254.         if (!BadBlock)              // if last block was OK
  1255.         {
  1256.             ++BlockNbr;
  1257.             ConsecErrors = 0;
  1258.             i = fread(buf, 1, BlockSize, fh);   // read a block in
  1259.             if (i == 0)             // if end of file
  1260.             {
  1261.                 rval = 1;           // exit value if EOT gets ACKd
  1262.                 while (1)
  1263.                 {
  1264.                     port.Tx(EOT);
  1265.                     if ((char)RxWithTimeOut(18) == ACK)
  1266.                         break;
  1267.                     if (i++ == 4)
  1268.                     {
  1269.                         rval = 2;   // exit value if EOT doesn't get ACKd
  1270.                         break;
  1271.                     }
  1272.                 }
  1273.                 break;
  1274.             }
  1275.             if (i < BlockSize)          // pad last block with CTL-Z's
  1276.                 memset(&buf[i], '\x1a', BlockSize - i);
  1277.         }
  1278.         else                            // had a bad block
  1279.         {
  1280.             if (++ConsecErrors == 10)
  1281.             {                           // 10 consecutive errors, abort
  1282.                 rval = ERROR_LIMIT;
  1283.                 continue;
  1284.             }
  1285.             vDisplay(4, 48, "%d", ConsecErrors);
  1286.             vDisplay(5, 48, "Got NAK for block %d", BlockNbr);
  1287.             if (!FirstAck)              // if not any good blocks yet
  1288.             {
  1289.                 if (BlockSize == 1024 && ConsecErrors == 2)
  1290.                 {
  1291.                     BlockSize = 128;    // Xmodem-1k isn't working,
  1292.                     BadBlock = 0;       // try 128 byte blocks
  1293.                     BlockNbr = '\0';
  1294.                     HdrChar = SOH;
  1295.                     rewind(fh);
  1296.                     continue;
  1297.                 }
  1298.                 if (UseCrc && ConsecErrors == 8)
  1299.                     UseCrc = 0;         // last ditch effort, try checksum
  1300.             }
  1301.         }
  1302.         port.Tx(HdrChar);               // send block header
  1303.         port.Tx(BlockNbr);
  1304.         port.Tx(~BlockNbr);
  1305.         port.Tx(buf, BlockSize);        // send file data
  1306.         if (UseCrc)
  1307.         {                               // send the CRC
  1308.             Checksum = calc_crc(buf, BlockSize);
  1309.             port.Tx((char)(Checksum >> 8));
  1310.             port.Tx((char)Checksum);
  1311.         }
  1312.         else
  1313.         {                               // if not CRC, send simple checksum
  1314.             Checksum = 0;
  1315.             pc = buf, end = pc + BlockSize;
  1316.             while (pc < end)
  1317.                 Checksum += (int)*pc++;
  1318.             port.Tx(Checksum);
  1319.         }
  1320.         ////////////////////
  1321.         //  wait for ACK  //
  1322.         ////////////////////
  1323.         ExtraNAK = 0;                    //---------------------------//
  1324.         while (!rval && !port.TxEmpty()) // while block is being sent //
  1325.         {                                //                           //
  1326.             if (port.RxLevel() != 0)     //  if something comes in    //
  1327.             {                            //                           //
  1328.                 LongCh = port.Rx();      //  get whatever it is       //
  1329.                                          //---------------------------//
  1330.                 //--------------------------------------------------//
  1331.                 //   If it's an ACK ignore it.  It may be due to a  //
  1332.                 // duplicate packet error but it is safer to make   //
  1333.                 // the receiver timeout and send another ACK after  //
  1334.                 // the packet has been sent.                        //
  1335.                 //   If it's a CAN, ignore that also and make them  //
  1336.                 // resend them at the end of the block.             //
  1337.                 //   An out of sequence NAK may be due to a block   //
  1338.                 // complement error.  Go ahead and take it but wait //
  1339.                 // for a few seconds after the block has finished   //
  1340.                 // transmitting to accept it as valid.              //
  1341.                 //--------------------------------------------------//
  1342.                 if ((char)LongCh == NAK)
  1343.                     ExtraNAK = 1;
  1344.             }
  1345.             if (KBHIT && KBREAD == X_ESC)   // monitor the keyboard
  1346.                 rval = ESC_PRESSED;
  1347.             if (!port.Carrier())            // monitor the carrier
  1348.                 rval = NO_CARRIER;
  1349.         }
  1350.         TimeOuts = 0;   //-------------------------------------------//
  1351.         while (!rval)   //  transmit buffer empty now, block is sent //
  1352.         {               //-------------------------------------------//
  1353.             LongCh = RxWithTimeOut(18);     // receive with 1 sec timeout
  1354.             if (LongCh >= 0)
  1355.             {
  1356.                 if ((char)LongCh == ACK)    // if got an ACK, have a good
  1357.                 {                           // block
  1358.                     FirstAck = 1;
  1359.                     BadBlock = 0;
  1360.                     BytesTransferred += (long)BlockSize;
  1361.                     vDisplay(3, 48, "%ld", BytesTransferred);
  1362.                     break;
  1363.                 }
  1364.                 if ((char)LongCh == NAK)    // if got a NAK, have a bad
  1365.                 {                           // block
  1366.                     BadBlock = 1;
  1367.                     break;
  1368.                 }
  1369.                 if ((char)LongCh == CAN && ++CANsRxd >= 2)
  1370.                     rval = GOT_CANNED;      // remote cancelled transfer
  1371.                 else
  1372.                     CANsRxd = 0;
  1373.                 continue;
  1374.             }
  1375.             if (LongCh == TIMED_OUT)
  1376.             {
  1377.                 if (++TimeOuts == 3 && ExtraNAK)
  1378.                 {                       // If had gotten a NAK during the //
  1379.                     BadBlock = 1;       // transmission of the block and  //
  1380.                     break;              // 3 secs have passed with no     //
  1381.                 }                       // chars, assume NAK was genuine  //
  1382.                 else if (TimeOuts >= 45)
  1383.                     rval = LongCh;      // if nothing after 45 secs, abort
  1384.             }
  1385.             else if (LongCh < 0)        // LongCh < 0 but not a timeout is
  1386.                 rval = LongCh;          // is a fatal error
  1387.         }
  1388.     }
  1389.     //////////////////////////////////////////////////////
  1390.     //  close file, free memory, reset params, & exit   //
  1391.     //////////////////////////////////////////////////////
  1392.     fclose(fh);
  1393.     delete buf;
  1394.     if (rval == ESC_PRESSED)
  1395.     {
  1396.         Display(5, 48, "Aborting file transfer ...");
  1397.         while (port.Carrier() && !port.TxEmpty())
  1398.             ;
  1399.         SendCANs();
  1400.     }
  1401.     ResetXmodemMode(save_xoff, save_flushbad, save_params);
  1402.     TransferWindow(0);
  1403.     XmodemExitMsg(rval);
  1404. }
  1405.  
  1406. /////////////////////////////////////////////////
  1407. //  XmodemExitMsg                              //
  1408. //://////////////////////////////////////////////
  1409. void XmodemExitMsg(int rval)
  1410. {
  1411.     switch (rval)                           // display exit message
  1412.     {
  1413.       case 1:
  1414.         Display("\nFile transferred\n");
  1415.         break;
  1416.       case 2:
  1417.         Display("\nFile transferred, EOT unconfirmed\n");
  1418.         break;
  1419.       case TIMED_OUT:
  1420.         Display("\nRemote fails to respond\n");
  1421.         break;
  1422.       case GOT_CANNED:
  1423.         Display("\nRemote aborted transfer\n");
  1424.         break;
  1425.       case ERROR_LIMIT:
  1426.         Display("\nMaximum error count exceeded\n");
  1427.         break;
  1428.       case ESC_PRESSED:
  1429.         Display("File transfer aborted\n");
  1430.         break;
  1431.       case NO_CARRIER:
  1432.         Display("\nCarrier lost\n");
  1433.         break;
  1434.       default:
  1435.         Display("\nUnknown exit code\n");
  1436.         break;
  1437.     }
  1438. }
  1439.  
  1440. /////////////////////////////////////////////////
  1441. //  RxWithTimeOut                              //
  1442. //://////////////////////////////////////////////
  1443. int RxWithTimeOut(int Ticks)
  1444. {
  1445.     int LongCh;
  1446.  
  1447.     if (!(B_RXEMPTY & (LongCh = port.Rx())))
  1448.         return (LongCh & 0xff);         // fast return if char available
  1449.  
  1450.     Timer t(Ticks);                     // start timeout timer
  1451.     while (1)
  1452.     {
  1453.         if (port.RxLevel())
  1454.             return (port.Rx() & 0xff);  // return char if available
  1455.         if (!port.Carrier())
  1456.             return NO_CARRIER;          // return if carrier was lost
  1457.         if (KBHIT && KBREAD == X_ESC)
  1458.             return ESC_PRESSED;         // return if ESC was pressed
  1459.         if (t.Expired())
  1460.             return TIMED_OUT;           // return if Ticks have expierd
  1461.     }
  1462. }
  1463.  
  1464. /////////////////////////////////////////////////
  1465. //  SetXmodemMode                              //
  1466. //://////////////////////////////////////////////
  1467. void SetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params)
  1468. {
  1469.     char new_params[10], *pc;
  1470.  
  1471.     save_xoff = port.XFlow();           // make sure XON/XOFF is turned off
  1472.     port.XFlow('\0');
  1473.     strcpy(save_params, port.Params()); // make sure params are N,8,1
  1474.     strcpy(new_params, save_params);
  1475.     for (pc = new_params; isdigit(*pc); pc++)
  1476.         ;                               // skip over baudrate
  1477.     strcpy(pc,"N81");                   // new_params = baudrate+"N81"
  1478.     port.Params(new_params);
  1479.     DisplayParameters();
  1480.     port.TxFlush();                     // flush the transmit side only
  1481.     save_flushbad = port.FlushBadChars();
  1482.     port.FlushBadChars(1);              // flush framing errors mode on
  1483. }
  1484.  
  1485. /////////////////////////////////////////////////
  1486. //  ResetXmodemMode                            //
  1487. //://////////////////////////////////////////////
  1488. void ResetXmodemMode(char& save_xoff, char& save_flushbad, char *save_params)
  1489. {
  1490.     port.XFlow(save_xoff);                  // reset to original XOFF mode
  1491.     port.FlushBadChars(save_flushbad);      // reset flush bad chars switch
  1492.     port.Params(save_params);               // back to original parameters
  1493.     DisplayParameters();
  1494. }
  1495.  
  1496. /////////////////////////////////////////////////
  1497. //  SendCANs                                   //
  1498. //://////////////////////////////////////////////
  1499. void SendCANs()
  1500. {
  1501.     port.Tx("\x18\x18\x18\x18\x18\b\b\b\b\b");  // send a bunch of control-X's
  1502.     while (port.Carrier() && !port.TxEmpty())
  1503.         ;
  1504.     port.RxFlush();
  1505. }
  1506.  
  1507. /////////////////////////////////////////////////
  1508. //  WaitForBlockToEnd                          //
  1509. //://////////////////////////////////////////////
  1510. int WaitForBlockToEnd(int Ticks)
  1511. {
  1512.     int LongCh;
  1513.  
  1514.     while ((LongCh = RxWithTimeOut(Ticks)) >= 0)
  1515.         ;                                       // wait for timeout or error
  1516.     return (LongCh == TIMED_OUT) ? 0 : LongCh;
  1517. }
  1518.  
  1519. /////////////////////////////////////////////////
  1520. //  GetHdrChar                                 //
  1521. //://////////////////////////////////////////////
  1522. int GetHdrChar(int Ticks)
  1523. {
  1524.     int LongCh, TimeOuts = 0, CANsRxd = 0;
  1525.  
  1526.     while (1)
  1527.     {
  1528.         LongCh = RxWithTimeOut(1);
  1529.         if ((char)LongCh == SOH || (char)LongCh == STX || (char)LongCh == EOT)
  1530.             break;                  // got block header
  1531.         if (LongCh == TIMED_OUT)
  1532.         {
  1533.             if (++TimeOuts > Ticks)
  1534.                 break;              // exceeded max Ticks
  1535.         }
  1536.         else if (LongCh < 0)
  1537.             break;                  // aborted, lost carrier, ...
  1538.         if ((char)LongCh == CAN && ++CANsRxd >= 2)
  1539.         {
  1540.             LongCh = GOT_CANNED;
  1541.             break;                  // remote cancelled
  1542.         }
  1543.         else
  1544.             CANsRxd = 0;            // garbage, reset CAN counter
  1545.     }
  1546.     return (LongCh);
  1547. }
  1548.  
  1549. /////////////////////////////////////////////////
  1550. //  TransferWindow                             //
  1551. //://////////////////////////////////////////////
  1552. void TransferWindow(int Flag)
  1553. {
  1554.     if (Flag == 0)
  1555.     {
  1556.         RemoveBox();
  1557.         return;
  1558.     }
  1559.     DrawBox(0, 26, 7, 53);
  1560.     v_color = ylw;
  1561.     if (Flag > 0)
  1562.         Display(1, 34, "Transmitting:");
  1563.     else
  1564.         Display(1, 37, "Receiving:");
  1565.     Display(2, 35, "Total Bytes:");
  1566.     Display(3, 29, "Bytes Transferred:");
  1567.     Display(4, 28, "Consecutive Errors:");
  1568.     Display(5, 36, "Last Error:");
  1569.     v_color = wht;
  1570. }
  1571.  
  1572.